package com.synjones.face.camera;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.util.Log;
import android.util.Pair;
import android.view.TextureView;
import android.widget.Toast;

import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.VersionInfo;
import com.blankj.utilcode.util.ToastUtils;
import com.synjones.face.model.DrawInfo;
import com.synjones.util.ImageUtil;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class FaceHelper {

    private final String ARC_FACE_APP_ID = "D5XFQLXfYWuQdLZt35BW86rXsyZsqjou5oXRMPNP3zPR";
    private final String ARC_FACE_SDK_KEY = "8LYh9imRYw7zVph1QhA2K9H6mvzseGjNUFfB9WW2oQho";

    private boolean initSuccess = false;

    private final String TAG = "FaceHelper";
    private TextureView previewView;
    private FaceRectView cameraFace;
    private CameraHelper cameraHelper;
    private Context context;
    private Activity activity;
    private Camera.Size previewSize;
    private DrawHelper drawHelper;
    private FaceEngine faceEngineCaputrue;
    private FaceEngine faceEngineCompare;
    private int processMask = FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE
            | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS;
    //用于获取人脸特征提取成功的帧
    private ExecutorService singleExecutors = Executors.newSingleThreadExecutor();

    private LinkedBlockingQueue<Pair<byte[],List<FaceInfo>>> captureQue = new LinkedBlockingQueue<>(1);

    public FaceHelper(TextureView previewView,FaceRectView faceRectView, Context context,Activity activity) {
        this.previewView = previewView;
        this.cameraFace = faceRectView;
        this.context = context;
        this.activity = activity;
        initFaceEngine();
    }


    public void initFaceEngine(){
        faceEngineCaputrue = new FaceEngine();
        faceEngineCompare = new FaceEngine();
        int afCode = faceEngineCaputrue.active(context, ARC_FACE_APP_ID,ARC_FACE_SDK_KEY);
        if (afCode == ErrorInfo.MOK || afCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
            afCode = faceEngineCaputrue.init(context.getApplicationContext(), FaceEngine.ASF_DETECT_MODE_VIDEO, FaceEngine.ASF_OP_0_ONLY,
                    16, 20, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS);
            VersionInfo versionInfo = new VersionInfo();
            faceEngineCaputrue.getVersion(versionInfo);
            Log.i(TAG, "Face initEngine:  init:"+afCode +" version:"+versionInfo.getVersion());
            if (afCode != ErrorInfo.MOK) {
                ToastUtils.showShort("人脸捕捉引擎初始化失败", Toast.LENGTH_SHORT);
            } else {
                initCamera();
            }
        }
        afCode = faceEngineCompare.active(context, ARC_FACE_APP_ID,ARC_FACE_SDK_KEY);
        if (afCode == ErrorInfo.MOK || afCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
            afCode = faceEngineCompare.init(context.getApplicationContext(), FaceEngine.ASF_DETECT_MODE_IMAGE, FaceEngine.ASF_OP_0_ONLY,
                    8, 2, FaceEngine.ASF_FACE_DETECT |FaceEngine.ASF_FACE_RECOGNITION);
            VersionInfo versionInfo = new VersionInfo();
            faceEngineCompare.getVersion(versionInfo);
            Log.i(TAG, "Face initEngine:  init:"+afCode +" version:"+versionInfo.getVersion());
            if (afCode != ErrorInfo.MOK) {
                ToastUtils.showShort("人脸对比引擎初始化失败", Toast.LENGTH_SHORT);
            }else {
                initSuccess = true;
            }
        }
    }

    private void initCamera() {
        CameraListener cameraListener = new CameraListener() {
            @Override
            public void onCameraOpened(Camera camera, final int cameraId,
                                       final int displayOrientation, final boolean isMirror) {
                Log.i(TAG, "onCameraOpened: " + cameraId + "  " + displayOrientation + " " + isMirror);
                previewSize = camera.getParameters().getPreviewSize();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        drawHelper = new DrawHelper(previewSize.width, previewSize.height,
                                previewView.getWidth(), previewView.getHeight(),
                                displayOrientation, cameraId, true,false,false);
                    }
                }).start();

            }


            @Override
            public void onPreview(byte[] nv21, Camera camera) {

                if (cameraFace != null) {
                    cameraFace.clearFaceInfo();
                }
                List<FaceInfo> faceInfoList = new ArrayList<>();
                int code = faceEngineCaputrue.detectFaces(nv21, previewSize.width, previewSize.height,
                        FaceEngine.CP_PAF_NV21, faceInfoList);
                if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
                    code = faceEngineCaputrue.process(nv21, previewSize.width, previewSize.height,
                            FaceEngine.CP_PAF_NV21, faceInfoList, processMask);
                    if (code != ErrorInfo.MOK) {
                        return;
                    }
                }else {
                    return;
                }

                List<AgeInfo> ageInfoList = new ArrayList<>();
                List<GenderInfo> genderInfoList = new ArrayList<>();
                List<Face3DAngle> face3DAngleList = new ArrayList<>();
                List<LivenessInfo> faceLivenessInfoList = new ArrayList<>();
                int ageCode = faceEngineCaputrue.getAge(ageInfoList);
                int genderCode = faceEngineCaputrue.getGender(genderInfoList);
                int face3DAngleCode = faceEngineCaputrue.getFace3DAngle(face3DAngleList);
                int livenessCode = faceEngineCaputrue.getLiveness(faceLivenessInfoList);

                //有其中一个的错误码不为0，return
                if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {
                    return;
                }

                try {
                    captureQue.offer(new Pair<>(nv21,getLargestFace(faceInfoList)),0,TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (cameraFace != null && drawHelper != null) {
                    List<DrawInfo> drawInfoList = new ArrayList<>();
                    for (int i = 0; i < faceInfoList.size(); i++) {
                        drawInfoList.add(
                                new DrawInfo(drawHelper.adjustRect(faceInfoList.get(i).getRect()),
                                genderInfoList.get(i).getGender(), ageInfoList.get(i).getAge(),
                                faceLivenessInfoList.get(i).getLiveness(), null));
                    }
                    drawHelper.draw(cameraFace, drawInfoList);
                }
            }

            @Override
            public void onCameraClosed() {
                Log.i(TAG, "onCameraClosed: ");
            }

            @Override
            public void onCameraError(Exception e) {
                Log.i(TAG, "onCameraError: " + e.getMessage());
            }

            @Override
            public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
                if (drawHelper != null) {
                    drawHelper.setCameraDisplayOrientation(displayOrientation);
                }
                Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + "  " + displayOrientation);
            }
        };
        cameraHelper = new CameraHelper.Builder()
                .previewViewSize(new Point(previewView.getMeasuredWidth(),previewView.getMeasuredHeight()))
                .rotation(activity.getWindowManager().getDefaultDisplay().getRotation())
                .specificCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT)
                .isMirror(true)
                .previewOn(previewView)
                .cameraListener(cameraListener)
                .build();
        cameraHelper.init();
        cameraHelper.start();
    }

    public Bitmap compareIdcard(Bitmap  bitmapSrc){
        Bitmap resizeSrc = resizeBitmap(bitmapSrc);
        final FaceFeature feature = getIdFeature(resizeSrc);
        Future<Pair<byte[], Rect>> task = null;
        task = singleExecutors.submit(new Callable<Pair<byte[], Rect>>() {
            @Override
                public Pair<byte[], Rect> call() throws Exception {
                //去掉队列里过时的帧
                captureQue.take();
                while (true) {
                    Pair<byte[],List<FaceInfo>> pair = captureQue.take();
                    byte[] data = pair.first;
                    List<FaceInfo> aftFaceList = pair.second;
                    FaceFeature faceFeature2 = new FaceFeature();
                    int resultCode = faceEngineCompare.extractFaceFeature(data, previewSize.width, previewSize.height,
                            FaceEngine.CP_PAF_NV21, aftFaceList.get(0), faceFeature2);
                    if (resultCode == ErrorInfo.MOK) {
                        FaceSimilar faceSimilar = new FaceSimilar();
                        int similarResultCode = faceEngineCompare.compareFaceFeature(feature, faceFeature2, faceSimilar);
                        if (similarResultCode == ErrorInfo.MOK && faceSimilar.getScore() >= 0.6) {
                            Log.e(TAG, "compare score is " + faceSimilar.getScore());
                            return new Pair<>(data, aftFaceList.get(0).getRect());
                        }
                    }
                }
            }
        });
        try {
            Pair<byte[], Rect> pair =  task.get(1000, TimeUnit.MILLISECONDS);
            if (pair!=null){
                YuvImage yuv = new YuvImage(pair.first, ImageFormat.NV21, previewSize.width, previewSize.height, null);
                ByteArrayOutputStream ops = new ByteArrayOutputStream();
                yuv.compressToJpeg(pair.second, 60, ops);
                byte[] data = ops.toByteArray();
                return BitmapFactory.decodeByteArray(data, 0, data.length);
            }
        } catch ( Exception e) {
            //超时返回识别失败
            Log.i(TAG, "对比人脸超时");
            return null;
        } finally {
            task.cancel(true);
        }
        return null;
    }

    private Bitmap resizeBitmap(Bitmap bitmap) {
        int outOfWidth = bitmap.getWidth() % 4;
        int outOfHeight = bitmap.getHeight() % 2;
        if (outOfHeight == 0 && outOfWidth == 0) return bitmap;
        int width = bitmap.getWidth() - outOfWidth;
        int height = bitmap.getHeight() - outOfHeight;
        return Bitmap.createBitmap(bitmap, 0, 0, width, height);
    }

    private FaceFeature getIdFeature(Bitmap bitmap)  {
        FaceFeature faceFeature1 = new FaceFeature();
        byte[] data = ImageUtil.bitmapToNv21(bitmap, bitmap.getWidth(), bitmap.getHeight());
        List<FaceInfo> faceList = new ArrayList<>();
        int detectRltCode = faceEngineCompare.detectFaces(data, bitmap.getWidth(), bitmap.getHeight(), FaceEngine.CP_PAF_NV21, faceList);
        if (detectRltCode != ErrorInfo.MOK || faceList.isEmpty()) {
        }
        int resultCode = faceEngineCompare.extractFaceFeature(data, bitmap.getWidth(), bitmap.getHeight(),
                FaceEngine.CP_PAF_NV21, faceList.get(0), faceFeature1);
        if (resultCode != ErrorInfo.MOK) {
        }
        return faceFeature1;
    }

    public boolean isInitSuccess() {
        return initSuccess;
    }

    public void release(){
        faceEngineCaputrue.unInit();
        faceEngineCompare.unInit();
    }

    /**
     * @param faceInfoList 这个集合必须大于0
     * @return
     */
    private List<FaceInfo> getLargestFace(List<FaceInfo> faceInfoList){
        FaceInfo faceInfo = faceInfoList.get(0);
        int size = faceInfoList.size();
        for (int i = 1; i < size; i++) {
            Rect oldRect = faceInfo.getRect();
            Rect newRect = faceInfoList.get(i).getRect();
            int newArea = (newRect.right - newRect.left) * (newRect.bottom - newRect.top);
            int oldArea = (oldRect.right - oldRect.left) * (oldRect.bottom - oldRect.top);
            if ( oldArea < newArea) {
                faceInfo = faceInfoList.get(i);
            }
        }
        List<FaceInfo> rltList = new ArrayList<>();
        rltList.add(faceInfo);
        return rltList;
    }
}
